package com.jimmyhsu.ecnudaowei.coursetable.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.TextView;
import android.widget.Toast;

import com.jimmyhsu.ecnudaowei.Bean.TaskActivity;
import com.jimmyhsu.ecnudaowei.R;
import com.jimmyhsu.ecnudaowei.coursetable.edit.AddOrEditCourseActivity;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Random;

/**
 * Created by jimmyhsu on 2017/3/11.
 */

public class CourseTableLayout extends ViewGroup {

    public static final int DAY_COUNT = 7;
    public static final int DAY_COURSE_COUNT = 10;
    private final String TAG = "CourseTableLayout";
    private int currWeek = 1;
    private int maxWeek = 19;
    private int mWidth;
    private int mHeight;
    private float mRowHeight;
    private int mTextColor = 0xFF1E3F5C;
    private int mBackgroundDark = 0xFFF2F8F7;
    private int mTodayBackground = 0xFFABF4EE;
    private int mDividerColor = 0xFFE4ECEE;
    private int mRowIndicatorColor = 0xFF3E5C77;
    private int mTextSizeCourseInSP = 13;
    private int mTextSmallSize = 11;
    private int mTextBigSize = 16;


    private boolean isModifyMode = false;
    private int mSelectedDay = -1;
    private List<Integer> mSelectedNumbers = new ArrayList<>();
    private int mLastSelectedNumber = -1;

    private static final String MONTH = "月";
    public static final String[] NUMBERS = new String[]{"一", "二", "三", "四", "五", "六", "日"};
    private static final int[] CARD_COLORS = new int[]{0xFFE4E9F1, 0xFF70C3F7, 0xFFFDB650,
            0xFF47DFC6, 0xFF70C3F7, 0xFF43D894, 0xFF70C3F7, 0xFFFB9EC9};

    private Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private float mTopHeight;
    private int mTopFrameMargin = dp2px(5, getContext());
    private float mFirstColWidth;
    private float mColWidth;
    private Typeface mTypeface;

    private boolean isMeasureComplete = false;
    private boolean needAddView = false;
    private List<TaskActivity> mCourseTable = new ArrayList<>();

    private OnCourseClickedListner mListner;

    public CourseTableLayout(Context context) {
        this(context, null);
    }

    public CourseTableLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public CourseTableLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);
        setBackgroundColor(0xffffffff);
        mTypeface = Typeface.createFromAsset(context.getAssets(), "Roboto-Condensed.ttf");
        setClickable(true);
    }

    public void setOnCourseClickListner(OnCourseClickedListner listner) {
        mListner = listner;
    }

    public void setCurrentWeek(int weekNo) {
        this.currWeek = weekNo - 1;
        addCourseView();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
        getColWidths();
        getTopHeight();
        mRowHeight = (mHeight - mTopHeight) / DAY_COURSE_COUNT;
        isMeasureComplete = true;

    }

    public void enterEditorMode() {
        this.isModifyMode = true;
        enterModify();
    }

    public void exitEditorMode(boolean animated) {
        this.isModifyMode = false;
        mSelectedDay = -1;
        mSelectedNumbers.clear();
        mLastSelectedNumber = -1;
        mLastIllegalNum = -1;
        invalidate();
        exitModify(animated);
    }


    public boolean isInEditorMode() {
        return isModifyMode;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawFrame(canvas);
        if (!mSelectedNumbers.isEmpty() && mSelectedDay != -1) {
            drawSelectedGrid(canvas);
        }
        if (needAddView) {
            addCourseView();
            needAddView = false;
        }
    }

    private void drawSelectedGrid(Canvas canvas) {
        for (int i = 0; i < mSelectedNumbers.size(); i++) {
            int num = mSelectedNumbers.get(i);
            int l = (int) (mSelectedDay * mColWidth + mFirstColWidth);
            int t = (int) (num * mRowHeight + mTopHeight);
            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setColor(0xFFDAE5EE);
            paint.setStyle(Paint.Style.FILL);
            canvas.drawRect(l, t, l + mColWidth, t + mRowHeight, paint);

        }
    }

    private void enterModify() {
        final int cCount = getChildCount();
        ValueAnimator animator = ValueAnimator.ofFloat(1, 0.8f)
                .setDuration(300);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float val = (float) animation.getAnimatedValue();
                for (int i = 0; i < cCount; i++) {
                    getChildAt(i).setAlpha(val);
                    getChildAt(i).setScaleX(val);
                    getChildAt(i).setScaleY(val);
                }
            }
        });
        animator.setInterpolator(new DecelerateInterpolator());
        animator.start();
    }

    private void exitModify(boolean animated) {
        if (!animated) return;
        final int cCount = getChildCount();
        ValueAnimator animator = ValueAnimator.ofFloat(0.8f, 1)
                .setDuration(200);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float val = (float) animation.getAnimatedValue();
                for (int i = 0; i < cCount; i++) {
                    getChildAt(i).setAlpha(val);
                    getChildAt(i).setScaleX(val);
                    getChildAt(i).setScaleY(val);
                }
            }
        });
        animator.setInterpolator(new AccelerateInterpolator());
        animator.start();
    }

    private void drawFrame(Canvas canvas) {
        drawUpperBackground(canvas);
        drawUpperText(canvas);
        drawLeftColumn(canvas);
        drawDashPath(canvas);
    }

    private void drawDashPath(Canvas canvas) {
        DashPathEffect dashPathEffect = new DashPathEffect(
                new float[]{dp2px(6, getContext()), dp2px(6, getContext())}, 1);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(mBackgroundDark);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeWidth(dp2px(2, getContext()));
        paint.setPathEffect(dashPathEffect);
        for (int i = 1; i < 10; i++) {
            Path path = new Path();
            path.moveTo(mFirstColWidth, mTopHeight + i * mRowHeight);
            path.lineTo(mWidth, mTopHeight + i * mRowHeight);
            canvas.drawPath(path, paint);
        }
    }

    private void drawLeftColumn(Canvas canvas) {
        canvas.drawRect(0, mTopHeight, mFirstColWidth, mHeight, mBackgroundPaint);
        mRowHeight = (mHeight - mTopHeight) / DAY_COURSE_COUNT;
        mTextPaint.setColor(mRowIndicatorColor);
        mTextPaint.setTypeface(mTypeface);
        mTextPaint.setTextSize(sp2px(13, getContext()));
        int textHeight = (int) (mTextPaint.getFontMetrics().descent - mTextPaint.getFontMetrics().ascent);
        for (int i = 1; i < 11; i++) {
            canvas.drawText(String.valueOf(i),
                    mFirstColWidth / 2f - getTextWidth(String.valueOf(i), mTextPaint) / 2f,
                    mTopHeight + (i - 1) * mRowHeight + mRowHeight / 2 + textHeight / 2,
                    mTextPaint);
        }
    }

    private float getTextWidth(String src, Paint textPaint) {
        Rect rect = new Rect();
        textPaint.getTextBounds(src, 0, src.length(), rect);
        return rect.width();
    }

    private void drawUpperBackground(Canvas canvas) {
        mBackgroundPaint.setColor(mBackgroundDark);
        mBackgroundPaint.setStyle(Paint.Style.FILL);
        canvas.drawRect(0, 0, mWidth, mTopHeight, mBackgroundPaint);
    }

    private void drawUpperText(Canvas canvas) {
//        getColWidths();
        Calendar calendar = Calendar.getInstance();
        String currMonth = String.valueOf(calendar.get(Calendar.MONTH) + 1) + "\r\n月";
        drawText(canvas, 12, currMonth, 0);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
        String[] dates = new String[7];
        int today = initDays(dates, dayOfWeek, dayOfMonth);
        for (int i = 0; i < 7; i++) {
            if (i == today) {
                drawTodayRect(canvas, i);
            }
            drawText(canvas, i == today ? mTextBigSize : mTextSmallSize,
                    dates[i], mFirstColWidth + mColWidth * i);
        }
        drawDivider(canvas);
    }

    private void drawTodayRect(Canvas canvas, int index) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(mTodayBackground);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawRect(mFirstColWidth + index * mColWidth,
                0,
                mFirstColWidth + (index + 1) * mColWidth,
                mTopHeight,
                paint);
    }

    private void drawDivider(Canvas canvas) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(mDividerColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(1);
        canvas.drawLine(0, mTopHeight, mWidth, mTopHeight, paint);
    }

    private int initDays(String[] dst, int dayOfWeek, int dayOfMonth) {
        int index = dayOfWeek - 2 >= 0 ? dayOfWeek - 2 : 6;
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        int tempIndex = index;
        int count = 0;
        while (index > -1) {
            dst[index] = "周" + NUMBERS[index] + "\r\n" + c.get(Calendar.DAY_OF_MONTH) + "日";
            index--;
            c.add(Calendar.DATE, -1);
            count++;
        }
        index = tempIndex + 1;
        c.add(Calendar.DATE, count + 1);
        while (index < 7) {
            dst[index] = "周" + NUMBERS[index] + "\r\n" + c.get(Calendar.DAY_OF_MONTH) + "日";
            index++;
            c.add(Calendar.DATE, 1);
        }
        return dayOfWeek - 2 >= 0 ? dayOfWeek - 2 : 6;
    }

    private void drawText(Canvas canvas, float textSize, String text, float transX) {

        mTextPaint.setTextSize(sp2px(textSize, getContext()));
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTypeface(mTypeface);
        String line1 = text.substring(0, text.indexOf("\r\n"));
        String line2 = text.substring(text.indexOf("\r\n") + 2);
        float line1Width = getTextWidth(line1, mTextPaint);
        float line2Width = getTextWidth(line2, mTextPaint);
        Paint.FontMetrics fm = mTextPaint.getFontMetrics();
        float textHeight = fm.descent - fm.ascent;
        float mVerticalMargin = (mTopHeight - 2 * textHeight) / 2f;
        float dp2 = dp2px(2, getContext()); // 消除toolbar阴影使文字看起来不垂直居中的影响
        if (textSize == mTextBigSize) {
            dp2 *= (mTextBigSize * 1.0f / mTextSmallSize);
        }
        if (line1.length() == 1) {
            canvas.drawText(line1, transX + mFirstColWidth / 2f - line1Width / 2f, mVerticalMargin + textHeight - dp2, mTextPaint);
            canvas.drawText(line2, transX + mFirstColWidth / 2f - line2Width / 2f, mTopHeight / 2f + textHeight - dp2, mTextPaint);
        } else {
            canvas.drawText(line1, transX + mColWidth / 2f - line1Width / 2f, mVerticalMargin + textHeight - dp2, mTextPaint);
            canvas.drawText(line2, transX + mColWidth / 2f - line2Width / 2f, mTopHeight / 2f + textHeight - dp2, mTextPaint);
        }
    }

    public void getColWidths() {
        mTextPaint.setTextSize(sp2px(16, getContext()));
        Rect textBounds = new Rect();
        mTextPaint.getTextBounds(MONTH, 0, MONTH.length(), textBounds);
        mFirstColWidth = textBounds.width() + 2 * mTopFrameMargin;
        mColWidth = (mWidth - mFirstColWidth) / 7f;
    }

    public void setCourses(List<TaskActivity> courseTable) {
        mCourseTable = courseTable;
        if (!isMeasureComplete) {
            needAddView = true;
            return;
        }
        addCourseView();
        invalidate();
    }

    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return isModifyMode;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isModifyMode) return super.onTouchEvent(event);
        int action = event.getAction();
        float x = event.getX();
        float y = event.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                selectSpace(x, y);
                break;
            case MotionEvent.ACTION_MOVE:
                selectSpace(x, y);
                break;
            case MotionEvent.ACTION_UP:
                gotoAddCourse();
                unSelectSpace();
                break;
        }
        return super.onTouchEvent(event);
    }

    private void gotoAddCourse() {
        int start = Math.min(mSelectedNumbers.get(0), mSelectedNumbers.get(mSelectedNumbers.size() - 1));
        int end = Math.max(mSelectedNumbers.get(0), mSelectedNumbers.get(mSelectedNumbers.size() - 1));
        List<String> weekData = getAvailWeek(mSelectedDay, start, end - start + 1);
        AddOrEditCourseActivity.startActivity(getContext(), mSelectedDay, start, end, weekData, maxWeek);
    }

    public List<String> getAvailWeek(int day, int num, int duration) {
        List<String> weekData = new ArrayList<>();
        for (int i = 0; i < maxWeek; i++) {
            weekData.add("第" + (i + 1) + "周");
        }
        for (TaskActivity course : mCourseTable) {
            if (course.getDay() == day &&
                    ((course.getNumber() >= num && course.getNumber() <= num + duration - 1)
                            || (course.getNumber() + course.getDuration() - 1 >= num && (course.getNumber() + course.getDuration() - 1 <= num + duration - 1)))) {
                for (int i = 0; i < maxWeek; i++) {
                    if (course.haveLessonAt(i)) {
                        weekData.remove("第" + (i + 1) + "周");
                    }
                }
            }

        }
        return weekData;
    }

    public List<String> getAvailWeek(int day, int num, int duration, TaskActivity except) {
        List<String> weekData = new ArrayList<>();
        for (int i = 0; i < maxWeek; i++) {
            weekData.add("第" + (i + 1) + "周");
        }
        for (TaskActivity course : mCourseTable) {
            if (course.getCourseId().equals(except.getCourseId())) continue;
            if (course.getDay() == day &&
                    ((course.getNumber() >= num && course.getNumber() <= num + duration - 1)
                            || (course.getNumber() + course.getDuration() - 1 >= num && (course.getNumber() + course.getDuration() - 1 <= num + duration - 1)))) {
                for (int i = 0; i < maxWeek; i++) {
                    if (course.haveLessonAt(i)) {
                        weekData.remove("第" + (i + 1) + "周");
                    }
                }
            }

        }
        return weekData;
    }

    private void unSelectSpace() {
        if (mSelectedDay >= 0 || mSelectedNumbers.size() > 0) {
            mSelectedDay = mLastSelectedNumber = -1;
            mSelectedNumbers.clear();
            mLastIllegalNum = -1;
            invalidate();
        }
    }

    private int mLastIllegalNum = -1;

    private void selectSpace(float x, float y) {
        int dayIndex = (int) (Math.floor((x - mFirstColWidth) / mColWidth));
        int numIndex = (int) (Math.floor((y - mTopHeight) / mRowHeight));
        if ((mSelectedDay >= 0 && mSelectedDay != dayIndex) ||
                (mSelectedNumbers.size() > 0 && mSelectedNumbers.get(mSelectedNumbers.size() - 1) == numIndex)) {
            mLastIllegalNum = -1;
            return;
        }
        if (numIndex == mLastSelectedNumber) {
            mSelectedNumbers.remove(mSelectedNumbers.size() - 1);
            invalidate();
            if (mSelectedNumbers.size() > 1) {
                mLastSelectedNumber = mSelectedNumbers.get(mSelectedNumbers.size() - 2);
            }
            mLastIllegalNum = -1;
            return;
        }
        if (dayIndex >= 0 && dayIndex < DAY_COUNT &&
                numIndex >= 0 && numIndex < DAY_COURSE_COUNT) {
            if (!checkConflict(dayIndex, numIndex)) {
                if (!checkContinuous(numIndex)) return;
                mLastSelectedNumber = mSelectedNumbers.size() > 0 ?
                        mSelectedNumbers.get(mSelectedNumbers.size() - 1) : -1;
                mSelectedDay = dayIndex;
                mSelectedNumbers.add(numIndex);
                mLastIllegalNum = -1;
                invalidate();
            } else {
                if (numIndex != mLastIllegalNum) {
                    Toast.makeText(getContext(), "这个时间已经被课占满了呢", Toast.LENGTH_SHORT).show();
                }
                mLastIllegalNum = numIndex;
            }
        }
    }

    private boolean checkContinuous(int numIndex) {
        if (mSelectedNumbers.size() > 0) {
            int first = mSelectedNumbers.get(0);
            int last = mSelectedNumbers.get(mSelectedNumbers.size() - 1);
            if (first >= last) {
                return numIndex >= last - 1 && numIndex <= first + 1;
            } else {
                return numIndex >= first - 1 && numIndex <= last + 1;
            }
        }
        return true;
    }

    private boolean checkConflict(int dayIndex, int numIndex) {
        boolean result = false;
        for (TaskActivity course : mCourseTable) {
            if (course.getDay() == dayIndex &&
                    course.getNumber() <= numIndex && course.getNumber() + course.getDuration() > numIndex &&
                    !course.getCourseWeek().substring(1, maxWeek + 1).contains("0")) {
                result = true;
                break;
            }
        }
        return result;
    }

    private void addCourseView() {
        removeAllViews();
        Random random = new Random();
        for (TaskActivity course : mCourseTable) {
            int bgColorIndex = course.haveLessonAt(currWeek) ? random.nextInt(CARD_COLORS.length - 1) + 1 : 0;
            TextView child = (TextView) getCourseView(course, bgColorIndex);
            child.setTag(course);
            int l = (int) (mFirstColWidth + course.getDay() * mColWidth + mTopFrameMargin / 2);
            int t = (int) (mTopHeight + course.getNumber() * mRowHeight + mTopFrameMargin / 2);
            int cWidth = (int) (mColWidth - mTopFrameMargin);
            int cHeight = (int) (mRowHeight * course.getDuration() - mTopFrameMargin);
            MarginLayoutParams p = new MarginLayoutParams(cWidth, cHeight);
            p.setMargins(l, t, 0, 0);
            child.setMaxWidth(cWidth);
            child.setEllipsize(TextUtils.TruncateAt.END);
            if (isModifyMode) {
                child.setAlpha(0.5f);
            }

            addView(child, p);
            child.layout(l, t, l + cWidth, t + cHeight);

        }
    }

    private View getCourseView(final TaskActivity course, int bgColorIndex) {
        TextView courseTv = new TextView(getContext());
        String courseInfo = course.getCourseName() + "@" + course.getLocation();
        courseInfo.replaceAll("\"", "");
        courseTv.setText(courseInfo);
        courseTv.setTextColor(bgColorIndex > 0 ? 0xffffffff : 0xFF8A9DB4);
        courseTv.setTextAlignment(TEXT_ALIGNMENT_CENTER);
        courseTv.setGravity(Gravity.CENTER);
        courseTv.setMaxLines(5);
        courseTv.setEllipsize(TextUtils.TruncateAt.valueOf("END"));
        courseTv.setBackground(ContextCompat.getDrawable(getContext(), R.drawable.course_bg_normal1));
        GradientDrawable background = (GradientDrawable) courseTv.getBackground();
        background.setColor(CARD_COLORS[bgColorIndex]);
        courseTv.setTypeface(mTypeface);
        courseTv.setTextSize(mTextSizeCourseInSP);
        courseTv.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mListner != null) {
                    mListner.onClick(v, (TaskActivity) v.getTag());
                }
            }
        });
        return courseTv;
    }

    private static int dp2px(float dp, Context context) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dp, context.getResources().getDisplayMetrics());
    }

    private static int sp2px(float sp, Context context) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                sp, context.getResources().getDisplayMetrics());
    }

    public float getTopHeight() {
        mTextPaint.setTextSize(sp2px(16, getContext()));
        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        float textHeight = (float) Math.ceil(fontMetrics.descent - fontMetrics.ascent);
        mTopHeight = 2 * mTopFrameMargin + 2 * textHeight;
        return mTopHeight;
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    public interface OnCourseClickedListner {
        void onClick(View courseView, TaskActivity courseInfo);
    }
}
